#include "Renderer.h"
#include <iostream>
#include "../external/external.h"

int main()
{
    using namespace Nitro;
    glfwInit();

    Renderer::OpenGLConfig openglConfig;
    openglConfig.samples = 16;
    openglConfig.versionMajor = 4;
    openglConfig.versionMinor = 5;
    Renderer::Init(openglConfig);

    auto window = glfwCreateWindow(600, 600, APP_NAME, 0, 0);
    glfwMakeContextCurrent(window);
    gladLoadGLLoader(GLADloadproc(glfwGetProcAddress));

    Renderer::ShaderDescriptor shaderDesc;
    shaderDesc.type = Renderer::VertexShader;
    shaderDesc.sourceType = Renderer::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    layout (location = 0) in vec2 pos;
    layout (location = 1) in vec2 texureCoord;
    uniform mat4 transform;
    out vec2 texCoord;
    void main() {
        gl_Position = transform * vec4(pos, 0.0f, 1.0f);
        texCoord = texureCoord;
    }
    )delimiter";
    auto vertexShader = Renderer::CreateShader(shaderDesc);
    if (vertexShader.hasReport())
    {
        std::cout << vertexShader.getReport();
    }
    shaderDesc.type = Renderer::FragmentShader;
    shaderDesc.sourceType = Renderer::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec2 texCoord;
    out vec4 fragColor;
    uniform sampler2D tex1;
    uniform sampler2D tex2;
    void main() {
        fragColor = mix(texture(tex1, texCoord), texture(tex2, texCoord), 0.5);
    }
    )delimiter";
    auto fragmentShader = Renderer::CreateShader(shaderDesc);
    if (fragmentShader.hasReport())
    {
        std::cout << fragmentShader.getReport();
    }

    Renderer::PipelineDescriptor pipelineDesc;
    pipelineDesc.vertexShader = vertexShader;
    pipelineDesc.fragmentShader = fragmentShader;
    pipelineDesc.drawMode = Renderer::Triangles;
    auto pipeline = Renderer::CreatePipeline(pipelineDesc);
    if (pipeline.hasReport())
    {
        std::cout << pipeline.getReport();
    }

    struct Vertex
    {
        float position[2], texCoord[2];
    } verts[] = {
        1.0f,  1.0f,  1.0f, 1.0f, //
        1.0f,  -1.0f, 1.0f, 0.0f, //
        -1.0f, -1.0f, 0.0f, 0.0f, //
        -1.0f, 1.0f,  0.0f, 1.0f, //
    };

    uint32_t indices[] = {0, 1, 2, 0, 2, 3};

    Renderer::BufferDescriptor bufferDesc;
    bufferDesc.bindFlags = Renderer::VertexBuffer;
    bufferDesc.size = sizeof(verts);
    bufferDesc.appendAttribute(0, sizeof(Vertex), Renderer::RG32Float);
    bufferDesc.appendAttribute(2 * sizeof(float), sizeof(Vertex),
                               Renderer::RG32Float);
    auto vertexBuffer = Renderer::CreateBuffer(bufferDesc, verts);

    bufferDesc.bindFlags = Renderer::IndexBuffer;
    bufferDesc.size = sizeof(indices);
    auto indexBuffer = Renderer::CreateBuffer(bufferDesc, indices);

    pipeline.activate();
    pipeline.setVertexBuffer(vertexBuffer);
    pipeline.setIndexBuffer(indexBuffer);

    glm::mat4 transform(1.0f);
    transform = glm::translate(transform, {0.0f, 0.0f, -0.1f});
    glm::mat4 projection = glm::perspective(60.0f, 1.0f, 0.01f, 100.0f);
    double prevTime, currentTime;
    prevTime = currentTime = glfwGetTime();

    int w, h, n;
    Renderer::SourceImageDescriptor srcDesc;
    Renderer::TextureDescriptor2D texDesc;
    Renderer::Texture2D tex1, tex2;
    uint8_t* data;

    data = stbi_load("1.jpg", &w, &h, &n, 3);
    srcDesc.data = data;
    srcDesc.format = Renderer::RGB8UInt;
    srcDesc.width = w;
    srcDesc.height = h;
    texDesc.minFilter = Renderer::LinearMipmapLinear;
    tex1 = Renderer::CreateTexture2D(texDesc, srcDesc);
    tex1.generateMipmap();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex1.id);
    stbi_image_free(data);

    data = stbi_load("2.jpg", &w, &h, &n, 3);
    srcDesc.data = data;
    srcDesc.format = Renderer::RGB8UInt;
    srcDesc.width = w;
    srcDesc.height = h;
    texDesc.minFilter = Renderer::LinearMipmapLinear;
    tex2 = Renderer::CreateTexture2D(texDesc, srcDesc);
    tex2.generateMipmap();
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex2.id);
    stbi_image_free(data);

    pipeline.setUniform<int>("tex1", 0);
    pipeline.setUniform<int>("tex2", 1);

    glfwSetWindowSizeCallback(
        window, [](auto, int w, int h) { glViewport(0, 0, w, h); });

    while (!glfwWindowShouldClose(window))
    {
        Renderer::Clear();
        Renderer::ClearColor();

        currentTime = glfwGetTime();

        pipeline.drawIndexed(0, 6);
        pipeline.setUniform("transform", projection * transform);

        prevTime = currentTime;

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}